#include "Dx11Base.h"

Dx11Base::Dx11Base(void) : m_driverType(D3D_DRIVER_TYPE_NULL), m_featureLevel(D3D_FEATURE_LEVEL_11_0),
  m_d3dDevice(0), m_d3dContext(0), m_swapChain(0), m_backBufferTarget(0), m_depthStencilView(0), m_depthTexture(0)
{
}


Dx11Base::~Dx11Base(void)
{
  this->Shutdown();
}


bool Dx11Base::LoadContent()
{
  return true;
}

void Dx11Base::UnloadContent()
{
}

void Dx11Base::Shutdown()
{
  this->UnloadContent();

  if(this->m_depthTexture)
    this->m_depthTexture->Release();

  if(this->m_depthStencilView)
    this->m_depthStencilView->Release();

  if(this->m_backBufferTarget)
    this->m_backBufferTarget->Release();

  if(this->m_swapChain)
    this->m_swapChain->Release();

  if(this->m_d3dContext)
    this->m_d3dContext->Release();

  if(this->m_d3dDevice)
    this->m_d3dDevice->Release();

  this->m_depthTexture = 0;
  this->m_depthStencilView = 0;
  this->m_backBufferTarget = 0;
  this->m_swapChain = 0;
  this->m_d3dContext = 0;
  this->m_d3dDevice = 0;
}

bool Dx11Base::Initialize(HINSTANCE hInstance, HWND hwnd)
{
  m_hInstance = hInstance;
  m_hwnd = hwnd;

  RECT dimensions;
  GetClientRect(m_hwnd, &dimensions);

  unsigned int width = dimensions.right - dimensions.left;
  unsigned int height = dimensions.bottom - dimensions.top;

  D3D_DRIVER_TYPE driverTypes[] = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_SOFTWARE, D3D_DRIVER_TYPE_REFERENCE};

  unsigned int totalDriverTypes = ARRAYSIZE(driverTypes);

  D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 };

  unsigned int totalFeatureLevels = ARRAYSIZE(featureLevels);

  DXGI_SWAP_CHAIN_DESC swapChainDesc;
  ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
  swapChainDesc.BufferCount = 1;
  swapChainDesc.BufferDesc.Width = width;
  swapChainDesc.BufferDesc.Height = height;
  swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
  swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
  swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  swapChainDesc.Windowed = true;
  swapChainDesc.SampleDesc.Count = 1;
  swapChainDesc.SampleDesc.Quality = 0;
  swapChainDesc.OutputWindow = m_hwnd;

  unsigned int creationFlags = 0;
#ifdef _DEBUG
  creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

  HRESULT result;

  for(unsigned int driver = 0; driver < totalDriverTypes; ++driver)
  {
    result = D3D11CreateDeviceAndSwapChain(0, driverTypes[driver], 0, creationFlags, featureLevels, totalFeatureLevels,
      D3D11_SDK_VERSION, &swapChainDesc, &m_swapChain, &m_d3dDevice, &m_featureLevel, &m_d3dContext);

    if(SUCCEEDED(result))
    {
      m_driverType = driverTypes[driver];
      break;
    }
  }

  if(FAILED(result))
  {
    DXTRACE_MSG("Failed to create the Direct3D device!");
    return false;
  }

  ID3D11Texture2D* backBufferTexture;
  result = m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*) &backBufferTexture);

  if(FAILED(result))
  {
    DXTRACE_MSG("Failed to get the swap chain back buffer!");
    return false;
  }

  result = m_d3dDevice->CreateRenderTargetView(backBufferTexture, 0, &m_backBufferTarget);

  if(backBufferTexture)
    backBufferTexture->Release();

  if(FAILED(result))
  {
    DXTRACE_MSG("Failed to create the render window!");
    return false;
  }

  D3D11_TEXTURE2D_DESC depthTexDesc;
  ZeroMemory( &depthTexDesc, sizeof( depthTexDesc ) );
  depthTexDesc.Width = width;
  depthTexDesc.Height = height;
  depthTexDesc.MipLevels = 1;
  depthTexDesc.ArraySize = 1;
  depthTexDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
  depthTexDesc.SampleDesc.Count = 1;
  depthTexDesc.SampleDesc.Quality = 0;
  depthTexDesc.Usage = D3D11_USAGE_DEFAULT;
  depthTexDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
  depthTexDesc.CPUAccessFlags = 0;
  depthTexDesc.MiscFlags = 0;

  result = m_d3dDevice->CreateTexture2D( &depthTexDesc, NULL, &this->m_depthTexture );

  if( FAILED( result ) )
  {
    DXTRACE_MSG( "Failed to create the depth texture!" );
    return false;
  }

  // Create the depth stencil view
  D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
  ZeroMemory( &descDSV, sizeof( descDSV ) );
  descDSV.Format = depthTexDesc.Format;
  descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
  descDSV.Texture2D.MipSlice = 0;

  result = this->m_d3dDevice->CreateDepthStencilView( this->m_depthTexture, &descDSV, &this->m_depthStencilView );

  if( FAILED( result ) )
  {
    DXTRACE_MSG( "Failed to create the depth stencil target view!" );
    return false;
  }

  m_d3dContext->OMSetRenderTargets(1, &m_backBufferTarget, 0);
  D3D11_VIEWPORT viewport;
  viewport.Width = static_cast<float>(width);
  viewport.Height = static_cast<float>(height);
  viewport.MinDepth = 0.0f;
  viewport.MaxDepth = 1.0f;
  viewport.TopLeftX = 0.0f;
  viewport.TopLeftY = 0.0f;

  m_d3dContext->RSSetViewports(1, &viewport);

  ZeroMemory( m_keyboardKeys, sizeof( m_keyboardKeys ) );
  ZeroMemory( m_prevKeyboardKeys, sizeof( m_prevKeyboardKeys ) );

  result = DirectInput8Create( m_hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, ( void** )&m_directInput, 0 );

  if( FAILED( result ) )
  { 
    return false;
  }

  result = m_directInput->CreateDevice( GUID_SysKeyboard, &m_keyboardDevice, 0 );

  if( FAILED( result ) )
  { 
    return false;
  }

  result = m_keyboardDevice->SetDataFormat( &c_dfDIKeyboard );

  if( FAILED( result ) )
  { 
    return false;
  }

  result = m_keyboardDevice->SetCooperativeLevel( m_hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE );

  if( FAILED( result ) )
  { 
    return false;
  }

  result = m_keyboardDevice->Acquire( );

  if( FAILED( result ) )
  { 
    return false;
  }

  m_mousePosX = m_mousePosY = m_mouseWheel = 0;

  result = m_directInput->CreateDevice( GUID_SysMouse, &m_mouseDevice, 0 );

  if( FAILED( result ) )
  { 
    return false;
  }

  result = m_mouseDevice->SetDataFormat( &c_dfDIMouse );

  if( FAILED( result ) )
  { 
    return false;
  }

  result = m_mouseDevice->SetCooperativeLevel( m_hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE );

  if( FAILED( result ) )
  { 
    return false;
  }

  result = m_mouseDevice->Acquire( );

  if( FAILED( result ) )
  { 
    return false;
  }


  return LoadContent();
}

bool Dx11Base::CompileD3DShader(char* filePath, char* entry, char* shaderModel, ID3DBlob** buffer)
{
  DWORD shaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;

#if defined(DEBUG) || defined(_DEBUG)
  shaderFlags |= D3DCOMPILE_DEBUG;
#endif

  ID3DBlob* errorBuffer = 0;
  HRESULT result = D3DX11CompileFromFile(filePath, 0, 0, entry, shaderModel, shaderFlags, 
    0, 0, buffer, &errorBuffer, 0);


  if(FAILED(result))
  {
    if(errorBuffer != 0)
    {
      OutputDebugStringA((char*) errorBuffer->GetBufferPointer());
      errorBuffer->Release();
    }
    return false;
  }

  if(errorBuffer != 0)
    errorBuffer->Release();

  return true;
}